package com.yikome.utils;

import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.TextNode;

import cn.hutool.core.util.StrUtil;

public class JSONUtils {
    private static ObjectMapper objectMapper;

    private JSONUtils() {
    }

    public static ObjectMapper getInstance() {
        if (objectMapper == null) {
            objectMapper = new ObjectMapper();
            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
            objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            

            //objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        }
        return objectMapper;
    }

    public static String toJsonString(final Object object) {
        try {
            return getInstance().writeValueAsString(object);
        } catch (final IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void writeJsonString(final Object object, final Writer writer) {
        try {
            getInstance().writeValue(writer, object);
        } catch (final IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static Map<?, ?> parseJson(final String jsonString) {
        JsonNode jn = null;
        try {
            jn = getInstance().readTree(jsonString);
        } catch (final IOException ex) {
            ex.printStackTrace();
        }
        return (Map<?, ?>) JsonNodeToMap(jn);
    }

    public static Object parseJson2MapOrList(final String jsonString) {
        JsonNode jn = null;
        try {
            jn = getInstance().readTree(jsonString);
        } catch (final IOException ex) {
            ex.printStackTrace();
        }
        return JsonNodeToMap(jn);
    }

    public static <T> T parseJson(final String jsonString, final Class<T> classType) {
        try {
            return getInstance().readValue(jsonString, classType);
        } catch (final Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static <T> T parseJson(final String jsonString, final TypeReference<T> typeReference) {
        try {
            return getInstance().readValue(jsonString, typeReference);
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static <T> T parseJsonT(final String jsonString) {
        try {
            return (T) getInstance().readValue(jsonString, new TypeReference<T>() {});
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static <T> Map<?, ?> bean2Map(final Object bean) {
        try {
            return getInstance().convertValue(bean, Map.class);
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static <T> T map2Bean(final Map<String, Object> map, final Class<T> clazz) {
        try {
            for (final Map.Entry<String, Object> kv : map.entrySet()) {
                if (kv.getValue() == null || "null".equals(kv.getValue())) {
                    kv.setValue("");
                }
            }
            return getInstance().convertValue(map, clazz);
        } catch (final Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    

    public static Object JsonNodeToMap(final JsonNode root) {
        final Map<String, Object> map = new LinkedHashMap<String, Object>();
        if (root == null) {
            return map;
        }
        if (root.isArray()) {
            final List<Object> list = new ArrayList<Object>();
            for (final JsonNode node : root) {
                final Object nmp = JsonNodeToMap(node);
                list.add(nmp);
            }
            return list;
        }
        if (root.isTextual()) {
            try {
                return ((TextNode) root).asText();
            } catch (final Exception e) {
                return root.toString();
            }
        }
        final Iterator<?> iter = root.fields();
        while (iter.hasNext()) {
            @SuppressWarnings("rawtypes")            
            final Map.Entry entry = (Map.Entry) iter.next();
            final String key = (String) entry.getKey();
            final JsonNode ele = (JsonNode) entry.getValue();
            if (ele.isObject()) {
                map.put(key, JsonNodeToMap(ele));
            } else if (ele.isTextual()) {
                map.put(key, ((TextNode) ele).asText());
            } else if (ele.isArray()) {
                map.put(key, JsonNodeToMap(ele));
            } else {
                map.put(key, ele.toString());
            }
        }
        return map;
    }

    public static String mapSerialize(final Map<String, Object> map) {
        final StringBuffer buffer = new StringBuffer();
        for (final Map.Entry<String, Object> kv : map.entrySet()) {
            buffer.append("&").append(kv.getKey()).append("=").append(kv.getValue());
        }
        String result = buffer.toString();
        if (!StrUtil.isBlank(result)) {
            result = result.substring(1);
        }
        return result;
    }

    public static Map<String, Object> cleanMap(final Map<String, Object> map) {
        final Map<String, Object> result = new HashMap<>(map);
        for (final Map.Entry<String, Object> kv : map.entrySet()) {
            if (kv.getValue() == null || StrUtil.isEmpty(String.valueOf(kv.getValue()))) {
                result.remove(kv.getKey());
            }
        }
        return result;
    }
    
    public static <T> List<T> parseJsonList(String jsonStr, Class<T> cls) {
        ObjectMapper mapper = new ObjectMapper();
        List<T> objList = null;
        try {
            JavaType t = mapper.getTypeFactory().constructParametricType(List.class, cls);
            objList = mapper.readValue(jsonStr, t);
        } catch (Exception e) {
        }
        return objList;
    }

    public static String getJsonNodeString(String jsonString, String nodeKey) {
        try {
            JsonNode jsonNode = getInstance().readTree(jsonString);
            JsonNode node = jsonNode.findValue(nodeKey);
            return getInstance().writeValueAsString(node);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
